import { JsonAsset, TextAsset, Asset } from "cc";
import { GameManager, regGameManager } from "../GameManager";
import { LoggerManager } from "./LoggerManager";
import { AssetsManager } from "./AssetsManager";

export class ConfigTable<T> {
    private _data_map = new Map()
    private _data_array: T[]
    constructor(dataTable) {
        this._data_array = dataTable
        dataTable.forEach((data, index) => {
            const keys = Object.keys(data)
            /**第一列作为id索引 */
            const id = data[keys[0]]
            this._data_map.set(id + "", data)
        });
    }
    /**获取第n行数据 */
    public getRow(rowIndex: number): T {
        return this._data_array[rowIndex]
    }
    /**根据索引获取一行数据 */
    public getDataById(id): T {
        return this._data_map.get(id + "")
    }
    public get array(): T[] {
        return this._data_array
    }
    public get length(): number {
        return this._data_array.length
    }
    /**获取最后一行数据 */
    get lastRow() {
        return this._data_array[this._data_array.length - 1]
    }
}


/**配置表管理器 */
@regGameManager()
export class ConfigManager extends GameManager {
    configMap = {}
    /**是否压缩配置文件 */
    isCompress = false

    defaultBundleName = "resources"
    /**初始化配置表管理器 */
    public async initialize(): Promise<void> {
        return new Promise(async (resolve) => {
            /**从数据源Config.dataMap中解析配置表 */
            const dataMap = (regConfig as any).dataMap;
            for (const key in dataMap) {
                //@ts-ignore
                let json = dataMap[key];
                this.setConfig(key, json);
            }
            resolve()
        });
        // return new Promise(
        //     async (resolve) => {


        //         /**从resources/config目录中加载配置表 */
        //         const bundle = await AssetsManager.instance().getBundle(this.defaultBundleName)
        //         if(bundle){
        //             bundle.loadDir(
        //                 "config",
        //                 async (e, assets) => {
        //                     if (e) {
        //                         LoggerManager.instance().log("load config error:", e);
        //                         resolve()
        //                         return
        //                     }
        //                     assets.forEach((asset) => {
        //                         this.setAsset(asset)
        //                     });
        //                     resolve()
        //                 });
        //         }else{
        //             resolve()

        //         }
        //     }
        // )
    }

    /**
     * 解压缩文本
     * 
     * 需要调用前需要将真正的解压函数替换到ConfigManager.decompress
     * 
     * @param text 待解压缩的文本字符串
     * @returns 解压缩后的文本字符串，当前情况下将直接返回输入的text
     */
    static decompress(text: string) {
        return text
    }
    /**根据分组设置配置资源数据 */
    setGroupAsset(asset: { name?: string; json?: any; text?: string }) {
        let textAsset = asset as TextAsset;
        const data = this.isCompress
            ? JSON.parse(ConfigManager.decompress(textAsset.text))
            : asset.json;
        const cfgNames = Object.keys(data);
        cfgNames.forEach((name) => {
            this.setConfig(name, data[name]);
        });
    }
    setAsset(asset: { name?: string, json?: any, text?: string }) {
        if (this.isCompress) {
            let asset2 = asset as TextAsset;
            this.setConfig(asset2.name, JSON.parse(ConfigManager.decompress(asset2.text)));
        } else {
            let asset2 = asset as JsonAsset;
            this.setConfig(asset2.name, asset2.json);
        }
    }

    /**
     * 设置配置数据的函数
     * @param {string} configFileName - 配置文件的名称，用作存储配置数据的属性名
     * @param {any} data - 要存储的配置数据，可以是数组或对象
     * 
     * 将传入的配置数据存储在当前对象的属性中，属性名由configFileName指定
     * 如果data是数组，会将其转换为ConfigTable实例再存储
     * 该函数主要用途是在配置文件加载时，将配置数据整理并存储在对象的属性中，以便后续使用
     */
    setConfig<T>(configFileName: string, data: any) {
        // 日志记录设置配置数据的操作，包括配置文件名和数据内容
        LoggerManager.instance().log("setConfig:", configFileName, data)
        // 判断传入的数据是否为数组
        if (data instanceof Array) {
            // 如果是数组，创建ConfigTable实例，并将其赋值给当前对象的属性
            this.configMap[configFileName] = new ConfigTable<T>(data)
        } else {
            // 如果不是数组，直接将数据赋值给当前对象的属性
            this.configMap[configFileName] = data;
        }
    }

    /**
     * 获取对象映射型配置表
     * @param config 
     * @returns 
     */
    public mapCfg<T>(config: ((new () => T))): T {
        return this.getCfgTableOrMapCfg(config)
    }


    /**
     * 获取列表型配置表中的一行
     * @param config 
     * @returns 
     */
    public cfgById<T>(config: ((new () => T)), id: number | string): T {
        return (this.getCfgTableOrMapCfg(config) as ConfigTable<T>).getDataById(id)
    }

    /**
     * 获取列表型配置表中的所有行
     * @param config 
     * @returns 
     */
    public cfgArr<T>(config: ((new () => T))): T[] {
        return (this.cfgTable(config) as ConfigTable<T>).array
    }
    /**
     * 获取列表型配置表
     * @param config 
     * @returns 
     */
    public cfgTable<T>(config: ((new () => T))): ConfigTable<T> {
        return this.getCfgTableOrMapCfg(config)
    }

    /**返回一个列表型配置表或映射表型配置表 */
    private getCfgTableOrMapCfg(config) {
        if (!(config as any).__configName) {
            throw new Error("该类型不是一个正确的表格，请检查是否使用了Config装饰器");
        }
        return this.configMap[(config as any).__configName]
    }

}

/**
 * 表格配置的类型装饰器
 * @param configName 配置表的名称(后缀加上Cfg)
 * @param isMap 是否是映射表
 * @param dataSource 配置表的数据源，不传就从MainCfg中获取
 * @returns 
 */
export function regConfig(configName: string, isMap = false, dataSource: any = null): ClassDecorator {
    //@ts-ignore
    if (!regConfig.clzMap) {
        //@ts-ignore
        regConfig.clzMap = {}
    }

    //手动提供的配置表数据源
    if (dataSource) {
        //@ts-ignore
        if (!regConfig.dataMap) {
            //@ts-ignore
            regConfig.dataMap = {}
        }
        //@ts-ignore
        regConfig.dataMap[configName] = dataSource

        ConfigManager.instance().setConfig(configName, dataSource);
    }


    return function (target) {
        //@ts-ignore
        regConfig.clzMap[configName] = target
        Object.defineProperty(
            target,
            '__configName',
            {
                value: configName,
                writable: false,
                enumerable: false
            }
        )

        Object.defineProperty(
            target,
            '__isMap',
            {
                value: isMap,
                writable: false,
                enumerable: false
            }
        )
    }
}
